/**
 * @file uart_printf.c
 * @author your name (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2023-09-08
 *
 * @copyright Copyright (c) 2023
 *
 */
#include "uart_printf.h"
#include "stm32f4xx_conf.h"
#include "FreeRTOS.h"
#include "semphr.h"

#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

/**
 * @brief  Configures the USART GPIO.
 * @param  None
 * @retval None
 */
void USART_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIOSturst;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);

    GPIOSturst.GPIO_Mode = GPIO_Mode_AF;
    GPIOSturst.GPIO_OType = GPIO_OType_PP;
    GPIOSturst.GPIO_Pin = GPIO_Pin_9;
    GPIOSturst.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIOSturst.GPIO_Speed = GPIO_Fast_Speed;
    GPIO_Init(GPIOA, &GPIOSturst);

    GPIOSturst.GPIO_Mode = GPIO_Mode_AF;
    GPIOSturst.GPIO_OType = GPIO_OType_OD;
    GPIOSturst.GPIO_Pin = GPIO_Pin_10;
    GPIOSturst.GPIO_PuPd = GPIO_PuPd_UP;
    GPIOSturst.GPIO_Speed = GPIO_Fast_Speed;
    GPIO_Init(GPIOA, &GPIOSturst);
}

/**
 * @brief  Configures the USART Peripheral.
 * @param  None
 * @retval None
 */
void USART_Printf_Config(void)
{
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    /* USARTx configured as follows:
          - BaudRate = 115200 baud
          - Word Length = 8 Bits
          - One Stop Bit
          - No parity
          - Hardware flow control disabled (RTS and CTS signals)
          - Receive and transmit enabled
    */
    USART_InitStructure.USART_BaudRate = 460800UL;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

    USART_Init(USART1, &USART_InitStructure);

    // 中断开启设置
    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);             // 开启检测串口空闲状态中断
    USART_ClearFlag(USART1, USART_FLAG_TC);                    // 清除USART2标志位
    USART_Cmd(USART1, ENABLE);                                 // 使能串口1
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;          // NVIC通道设置
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 10; // 抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;         // 响应优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            // IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);                            // 根据指定的参数初始化NVIC寄存器
}

void DMA2_USART1_Init(void)
{
    DMA_InitTypeDef DMA2_Init;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); // 使能DMA2时钟

    // DMA_USART1_TX
    DMA_DeInit(DMA2_Stream7);                                       // 将DMA的通道7寄存器重设为缺省值

    /* Check if the DMA Stream is disabled before enabling it.
         Note that this step is useful when the same Stream is used multiple times:
         enabled, then disabled then re-enabled... In this case, the DMA Stream disable
         will be effective only at the end of the ongoing data transfer and it will
         not be possible to re-configure it before making sure that the Enable bit
         has been cleared by hardware. If the Stream is used only once, this step might
         be bypassed. */
    while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE)
    {
    }

    DMA2_Init.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR);     // 启动传输前装入实际RAM地址
    DMA2_Init.DMA_Memory0BaseAddr = (uint32_t)NULL;                 // 设置发送缓冲区首地址
    DMA2_Init.DMA_DIR = DMA_DIR_MemoryToPeripheral;                 // 数据传输方向，从内存发送到外设
    DMA2_Init.DMA_BufferSize = 0;                                   // DMA通道的DMA缓存的大小
    DMA2_Init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // 外设地址寄存器不变
    DMA2_Init.DMA_MemoryInc = DMA_MemoryInc_Enable;                 // 内存地址寄存器递增
    DMA2_Init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 数据宽度为8位
    DMA2_Init.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         // 数据宽度为8位
    DMA2_Init.DMA_Mode = DMA_Mode_Normal;                           // 工作在正常模式
    DMA2_Init.DMA_Priority = DMA_Priority_High;                     // DMA通道 x拥有高优先级
    DMA2_Init.DMA_FIFOMode = DMA_FIFOMode_Disable;                  // 不使能FIFO模式
    DMA2_Init.DMA_MemoryBurst = DMA_MemoryBurst_Single;             // 突发模式1个字节
    DMA2_Init.DMA_Channel = DMA_Channel_4;                          // 通道4
    DMA2_Init.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;           // 全FIFO
    DMA2_Init.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;     // 外设突发传输1个节拍
    DMA_Init(DMA2_Stream7, &DMA2_Init);                             // 对DMA通道7进行初始化
    DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);                  // 开USART1 Tx DMA中断

    // DMA2通道7 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;   // NVIC通道设置
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 9; // 抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        // 子优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           // IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);                           // 根据指定的参数初始化NVIC寄存器
    
    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
}

// DMA 发送应用源码
void DMA_USART1_Tx_Data(const char *buffer, uint16_t size)
{
    DMA_MemoryTargetConfig(DMA2_Stream7, (uint32_t)buffer, DMA_Memory_0);
    DMA_SetCurrDataCounter(DMA2_Stream7, size);
    DMA_Cmd(DMA2_Stream7, ENABLE); // 开始DMA发送
}

// DMA1通道7中断
void DMA2_Stream7_IRQHandler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    if (DMA_GetITStatus(DMA2_Stream7, DMA_IT_TCIF7) != RESET) // DMA发送完成标志
    {
        DMA_ClearITPendingBit(DMA2_Stream7, DMA_IT_TCIF7); // 清除中断标志
        USART_ClearFlag(USART1, USART_FLAG_TC);            // 清除串口2的标志位
        DMA_Cmd(DMA2_Stream7, DISABLE);                    // 关闭USART2 TX DMA1 所指示的通道
        extern SemaphoreHandle_t xSemphore_dma_lock;
        xSemaphoreGiveFromISR(xSemphore_dma_lock, &xHigherPriorityTaskWoken);
        portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
    }
}

/**
 * @brief  Configures the USART Peripheral.
 * @param  None
 * @retval None
 */
void USART_Printf_Init(void)
{
    USART_GPIO_Config();
    USART_Printf_Config();
    DMA2_USART1_Init();
}

/**
 * @brief  Retargets the C library printf function to the USART.
 * @param  None
 * @retval None
 */
PUTCHAR_PROTOTYPE
{
    /* Place your implementation of fputc here */
    /* e.g. write a character to the USART */
    USART_SendData(USART1, (uint8_t)ch);

    /* Loop until the end of transmission */
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
    {
    }

    return ch;
}
